package com.gitee.wsl.func.filter.bloomfilter.api

import com.gitee.wsl.collections.map.TreeMap
import com.gitee.wsl.ext.base.compare
import com.gitee.wsl.func.predicate.IntPredicate
import com.gitee.wsl.func.predicate.Predicate


/**
 * Some Bloom filter implementations use a count rather than a bit flag. The term `Cell` is used to
 * refer to these counts and their associated index.  This class is the equivalent of the index extractor except
 * that it produces cells.
 *
 *
 * Note that a CellExtractor must not return duplicate indices and must be ordered.
 *
 *
 * Implementations must guarantee that:
 *
 *
 *  * The IndexExtractor implementation returns unique ordered indices.
 *  * The cells are produced in IndexExtractor order.
 *  * For every value produced by the IndexExtractor there will be only one matching
 * cell produced by the CellExtractor.
 *  * The CellExtractor will not generate cells with indices that are not output by the IndexExtractor.
 *  * The IndexExtractor will not generate indices that have a zero count for the cell.
 *
 *
 * @since 4.5.0-M2
 */
interface CellExtractor : IndexExtractor {
    /**
     * Represents an operation that accepts an `<index, count>` pair.
     * Returns `true` if processing should continue, `false` otherwise.
     *
     *
     * Note: This is a functional interface as a specialization of
     * [BiPredicate] for `int`.
     */
    fun interface CellPredicate {
        /**
         * Performs an operation on the given `<index, count>` pair.
         *
         * @param index the bit index.
         * @param count the cell value at the specified bit index.
         * @return `true` if processing should continue, `false` if processing should stop.
         */
        fun test(index: Int, count: Int): Boolean
    }

    /**
     * Performs the given action for each `cell`  where the cell count is non-zero.
     *
     *
     * Some Bloom filter implementations use a count rather than a bit flag.  The term `Cell` is used to
     * refer to these counts.
     *
     *
     * Any exceptions thrown by the action are relayed to the caller. The consumer is applied to each
     * cell. If the consumer returns `false` the execution is stopped, `false`
     * is returned, and no further pairs are processed.
     *
     * @param consumer the action to be performed for each non-zero cell.
     * @return `true` if all cells return true from consumer, `false` otherwise.
     * @throws NullPointerException if the specified consumer is null
     */
    fun processCells(consumer: CellPredicate): Boolean

    /**
     * The default implementation returns distinct and ordered indices for all cells with a non-zero count.
     */
    public override fun processIndices(predicate: IntPredicate): Boolean {
        return processCells(CellPredicate { i: Int, v: Int -> predicate.test(i) })
    }

    override fun uniqueIndices(): IndexExtractor {
        return this
    }

    companion object {
        /**
         * Creates a CellExtractor from an IndexExtractor.
         *
         *
         * Note the following properties:
         *
         *  * Each index returned from the IndexExtractor is assumed to have a cell value of 1.
         *  * The CellExtractor aggregates duplicate indices from the IndexExtractor.
         *
         *
         *
         * A CellExtractor that outputs the mapping [(1,2),(2,3),(3,1)] can be created from many combinations
         * of indices including:
         * <pre>
         * [1, 1, 2, 2, 2, 3]
         * [1, 3, 1, 2, 2, 2]
         * [3, 2, 1, 2, 1, 2]
         * ...
        </pre> *
         *
         * @param indexExtractor An index indexExtractor.
         * @return A CellExtractor with the same indices as the IndexExtractor.
         */
        fun from(indexExtractor: IndexExtractor): CellExtractor {
            return object : CellExtractor {
                /**
                 * Class to track cell values in the TreeMap.
                 */
                inner class CounterCell internal constructor(val idx: Int, var count: Int) : Comparable<CounterCell> {
                    override fun compareTo(other: CounterCell): Int {
                        return idx.compare(other.idx)
                    }
                }

                val counterCells: TreeMap<CounterCell, CounterCell> = TreeMap<CounterCell, CounterCell>()

                override fun asIndexArray(): IntArray {
                    populate()
                    return counterCells.keys.map({ c -> c.idx }).toIntArray()
                }

                fun populate() {
                    if (counterCells.isEmpty()) {
                        indexExtractor.processIndices(Predicate { idx: Int? ->
                            val cell = CounterCell(idx!!, 1)
                            val counter = counterCells[cell]
                            if (counter == null) {
                                counterCells.put(cell, cell)
                            } else {
                                counter.count++
                            }
                            true
                        })
                    }
                }

                override fun processCells(consumer: CellPredicate): Boolean {
                    populate()
                    for (cell in counterCells.values) {
                        if (!consumer.test(cell.idx, cell.count)) {
                            return false
                        }
                    }
                    return true
                }
            }
        }
    }
}

